# -*- coding: utf-8 -*-
# Version: Python 3.9.7
# Author: TRIX
# Date: 2021-09-12 21:18:39
# Use: 批量生成录用信(offer) 涉及到的模块应用包括 openpyxl random string win32com os datetime pyPDF3 随机生成n个面试者信息 包括 求职日期 姓名 年龄 性别 学历 电话 邮箱 职位 期望月薪 特长 把所有信息储存到Excel中 并从Excel中读取每个面试者的 求职日期 姓名 性别 学历 邮箱 职位 期望月薪 月薪转为rmb大写 随机录用或不被录用 批量生成信件 再批量转换为pdf文件 给信件每一页盖章

from openpyxl import Workbook
import openpyxl#需要安装 pip install openpyxl
from openpyxl.utils import get_column_letter

import datetime
import os

from random import choice,randint
import string

from win32com.client import Dispatch
import PyPDF3

#面试者信息数据
surnames='赵钱孙李周吴郑王冯陈褚卫蒋沈韩杨朱秦尤许何吕施张孔曹严华金魏陶姜戚谢邹喻柏水窦章云苏潘葛奚范彭郎鲁韦昌马苗凤花方俞任袁柳酆鲍史唐费廉岑薛雷贺倪汤滕殷罗毕郝邬安常乐于时傅皮卞齐康伍余元卜顾孟平黄和萧尹湛汪祁毛禹狄米贝成戴谈宋茅庞熊纪舒屈项祝董梁杜阮蓝闵席季麻强贾路娄危江童颜郭梅盛林刁钟徐邱骆高夏蔡田樊胡凌霍虞万支柯昝管卢莫白房裘缪干解应宗丁宣贲邓郁单杭洪包诸左石崔吉钮龚程嵇邢滑裴陆荣翁荀羊'#网上找的 195个姓
chars= "\u96d5\u864e\u4e00\u4e86\u662f\u4e0d\u5728\u4eba\u6709\u6765\u8fd9\u4e0a\u7740\u4e2a\u5730\u5230\u5927\u91cc\u8bf4\u5c31\u53bb\u5b50\u5f97\u4e5f\u548c\u90a3\u8981\u4e0b\u770b\u5929\u65f6\u8fc7\u51fa\u5c0f\u4e48\u8d77\u90fd\u628a\u597d\u8fd8\u591a\u6ca1\u4e3a\u53c8\u53ef\u5bb6\u5b66\u53ea\u4ee5\u4e3b\u4f1a\u6837\u5e74\u60f3\u751f\u540c\u8001\u4e2d\u5341\u4ece\u81ea\u9762\u524d\u5934\u9053\u5b83\u540e\u7136\u8d70\u5f88\u50cf\u89c1\u4e24\u7528\u5979\u56fd\u52a8\u8fdb\u6210\u56de\u4ec0\u8fb9\u4f5c\u5bf9\u5f00\u800c\u5df1\u4e9b\u73b0\u5c71\u6c11\u5019\u7ecf\u53d1\u5de5\u5411\u4e8b\u547d\u7ed9\u957f\u6c34\u51e0\u4e49\u4e09\u58f0\u4e8e\u9ad8\u624b\u77e5\u7406\u773c\u5fd7\u70b9\u5fc3\u6218\u4e8c\u95ee\u4f46\u8eab\u65b9\u5b9e\u5403\u505a\u53eb\u5f53\u4f4f\u542c\u9769\u6253\u5462\u771f\u5168\u624d\u56db\u5df2\u6240\u654c\u4e4b\u6700\u5149\u4ea7\u60c5\u8def\u5206\u603b\u6761\u767d\u8bdd\u4e1c\u5e2d\u6b21\u4eb2\u5982\u88ab\u82b1\u53e3\u653e\u513f\u5e38\u6c14\u9ec4\u4e94\u7b2c\u4f7f\u5199\u519b\u6728\u73cd\u5427\u6587\u8fd0\u518d\u679c\u600e\u5b9a\u8bb8\u5feb\u660e\u884c\u56e0\u522b\u98de\u5916\u6811\u7269\u6d3b\u90e8\u95e8\u65e0\u5f80\u8239\u671b\u65b0\u5e26\u961f\u5148\u529b\u5b8c\u5374\u7ad9\u4ee3\u5458\u673a\u66f4\u4e5d\u60a8\u6bcf\u98ce\u7ea7\u8ddf\u7b11\u554a\u5b69\u4e07\u5c11\u76f4\u610f\u591c\u6bd4\u9636\u8fde\u8f66\u91cd\u4fbf\u6597\u9a6c\u54ea\u5316\u592a\u6307\u53d8\u793e\u4f3c\u58eb\u8005\u5e72\u77f3\u6ee1\u6885\u65e5\u51b3\u767e\u539f\u62ff\u7fa4\u7a76\u5404\u516d\u672c\u601d\u89e3\u7acb\u6cb3\u6751\u516b\u96be\u65e9\u8bba\u5417\u6839\u5171\u8ba9\u76f8\u7814\u4eca\u5176\u4e66\u5750\u63a5\u5e94\u5173\u4fe1\u89c9\u6b65\u53cd\u5904\u8bb0\u5c06\u5343\u627e\u4e89\u9886\u6216\u5e08\u7ed3\u5757\u8dd1\u8c01\u8349\u8d8a\u5b57\u52a0\u811a\u7d27\u7231\u7b49\u4e60\u9635\u6015\u6708\u9752\u534a\u706b\u6cd5\u9898\u5efa\u8d76\u4f4d\u5531\u6d77\u4e03\u5973\u4efb\u4ef6\u611f\u51c6\u5f20\u56e2\u5c4b\u79bb\u8272\u8138\u7247\u79d1\u5012\u775b\u5229\u4e16\u521a\u4e14\u7531\u9001\u5207\u661f\u5bfc\u665a\u8868\u591f\u6574\u8ba4\u54cd\u96ea\u6d41\u672a\u573a\u8be5\u5e76\u5e95\u6df1\u523b\u5e73\u4f1f\u5fd9\u63d0\u786e\u8fd1\u4eae\u8f7b\u8bb2\u519c\u53e4\u9ed1\u544a\u754c\u62c9\u540d\u5440\u571f\u6e05\u9633\u7167\u529e\u53f2\u6539\u5386\u8f6c\u753b\u9020\u5634\u6b64\u6cbb\u5317\u5fc5\u670d\u96e8\u7a7f\u5185\u8bc6\u9a8c\u4f20\u4e1a\u83dc\u722c\u7761\u5174\u5f62\u91cf\u54b1\u89c2\u82e6\u4f53\u4f17\u901a\u51b2\u5408\u7834\u53cb\u5ea6\u672f\u996d\u516c\u65c1\u623f\u6781\u5357\u67aa\u8bfb\u6c99\u5c81\u7ebf\u91ce\u575a\u7a7a\u6536\u7b97\u81f3\u653f\u57ce\u52b3\u843d\u94b1\u7279\u56f4\u5f1f\u80dc\u6559\u70ed\u5c55\u5305\u6b4c\u7c7b\u6e10\u5f3a\u6570\u4e61\u547c\u97f3\u7b54\u54e5\u9645\u65e7\u795e\u5ea7\u7ae0\u5e2e\u5566\u53d7\u7cfb\u4ee4\u8df3\u975e\u4f55\u725b\u53d6\u5165\u5cb8\u6562\u6389\u5ffd\u79cd\u88c5\u9876\u6025\u6234\u6797\u505c\u606f\u53e5\u533a\u8863\u822c\u62a5\u53f6\u538b\u6162\u53d4\u80cc\u7ec6\u8273\u4f50"#网上找的 490个常用字 做名
ages=[x for x in range(20,45)]#年龄
nums='123456789'#数字
mailchars=string.digits*3+string.ascii_letters#邮箱
today_obj=datetime.date.today()#今天

def build_info():#建立面试者信息
	#随机求职日期
	date_obj=today_obj+datetime.timedelta(days=randint(-7,0))
	#随机姓名
	name=''
	name+=choice(surnames)#随机姓
	for n in range(choice([1,2,2])):#随机名
		name+=choice(chars)
	#随机年龄
	age=str(choice(ages))
	#随机性别
	gender=choice(['男','女'])
	#随机学历
	qualification=choice(['高中','专科','本科','本科','本科','硕士','硕士','博士'])
	#随机电话
	phone='1'
	for n in range(10):
		phone+=choice(nums)
	#随机邮箱
	mail=''
	for n in range(randint(4,10)):
		mail+=choice(mailchars)
	mail+='@'
	for n in range(randint(2,6)):
		mail+=choice(mailchars)
	mail+='.com'
	#随机职位
	position=''
	for n in range(randint(2,4)):
		position+=choice('ABCD')
	#随机期望月薪
	salary=choice(nums)
	for n in range(choice([3,3,3,3,4])):
		salary+='0'
	#随机特长
	skill=''
	for n in range(randint(10,30)):
		skill+=choice(chars)
	return [date_obj,name,age,gender,qualification,phone,mail,position,int(salary),skill]

def build_xlsx(count,xlsx_file):#把数据储存到xlsx 个数 输出文件
	xlsx=Workbook()#创建xlsx
	sheet=xlsx.active
	classification='求职日期 姓名 年龄 性别 学历 电话 邮箱 职位 期望月薪 特长'.split()
	sheet.append(classification)#增加分类栏

	for n in range(count):#随机创建面试者
		sheet.append(build_info())
	for n in range(1,len(classification)+1):
		sheet.column_dimensions[get_column_letter(n)].width = 15
	sheet.column_dimensions['G'].width = 25
	sheet.column_dimensions['J'].width = 50
	xlsx.save(xlsx_file)#只支持xlsx格式

def get_info_list(xlsxfile):#从Excel中读取每个面试者的 求职日期 姓名 性别 学历 邮箱 职位 期望月薪
	xlsx=openpyxl.load_workbook(xlsxfile,data_only=False)#打开xlsx文件 类的实例化 data_only=True返回单元格的计算后的值 False将返回单元格的原字符串
	sheet=xlsx.active#激活的sheet 相当于xlsx打开后默认显示的sheet
	info_list=[]
	for row in sheet.iter_rows(min_row=2,min_col=1,max_row=sheet.max_row,max_col=sheet.max_column,values_only=True):#指定行 values_only 只有值 没有坐标
		info_list.append([row[0],row[1],row[3],row[4],row[-4],row[-3],row[-2]])
	return info_list


def numberToRmb(strNum):#字符数字转RMB大写
	#基本字符
	float_unit_list=list('角分')#小数位大写列表
	rmb_list=list('零壹贰叁肆伍陆柒捌玖')#人民币数字大写列表
	unit_list=list('仟佰拾亿仟佰拾万仟佰拾元')#每一位的单位列表
	special_index_list=[3,7,-1]#亿 万 元 单位列表

	num_int_list=[]#处理时 把数字串分为 整数 小数列表
	num_float_list=[]
	rmb_num_int_list=[' 'for n in range(12)]#整数大写占位列表
	zheng=''#整
	rmb_num_float_list=[' ',' ']#小数大写占位列表
	result_int=''#结果整数值
	result_float=''#结果小数值

	if '.' not in strNum:#如果没有小数
		if len(strNum)>12:#如果输入值超过十二位提示错误
			return 'More than 12 digit integer cannot be converted! You entered a %s digit integer.'%len(strNum)
			#'%s'%* 表示把*转换为str后应用在%s处
		if len(strNum)<12:#如果小于十二位就先添加(12-输入值)这么多个的占位符
			num_int_list.extend(list(' ' for n in range(12-len(strNum))))
		num_int_list.extend(list(strNum))#添加输入值,列表形式
		num_float_list.extend([' ',' '])#没有小数,小数部分两个占位符
		zheng='整'#没有小数时,人民币大写格式为***元整
	elif '.' in strNum:#如果有小数
		num_split_list=strNum.split('.')#以小数点分隔
		if len(num_split_list[0])>12:#如果输入值整数位超过十二位提示错误
			return 'More than 12 digit integer cannot be converted! You entered a %s digit integer.'%len(num_split_list[0])
		if len(num_split_list[-1])>2:#如果输入值小数位超过十二位提示错误
			return 'More than 2 digit decimal cannot be converted! You entered a %s digit decimal.'%len(num_split_list[-1])
		if len(num_split_list[0])<12:#如果小于十二位就先添加(12-输入值)这么多个的占位符
			num_int_list.extend(list(' ' for n in range(12-len(num_split_list[0]))))
		num_int_list.extend(list(num_split_list)[0])
		num_float_list.extend(list(num_split_list)[-1])
		if len(num_float_list)==1:#小数位数是1的时候,添加一个占位符
			num_float_list.extend(' ')

	#数字转汉字
	for s in range(12):#循环12次,因为整数有12位
		if num_int_list[s]!=' ':#除了占位符外
			rmb_num_int_list[s]=rmb_list[int(num_int_list[s])]+unit_list[s]#数字+单位

	#处理特殊情况
	for s in range(11):#对于前11位
		if num_int_list[s]=='0' and num_int_list[s+1]=='0':#如果两个0连写,减少一个0
			rmb_num_int_list[s]=' '
		if s not in [3,7] and num_int_list[s]=='0' and num_int_list[s+1]!='0':#剔除亿位和万位后如果有零要去除单位
			rmb_num_int_list[s]=rmb_num_int_list[s][0]
	for s in special_index_list:
		if num_int_list[s]=='0':
			rmb_num_int_list[s]=unit_list[s]#亿 万 元 单位时特殊情况
	for s in range(2):#循环2次,因为小数有2位
		if num_float_list[s]!=' ':#除了占位符外
			rmb_num_float_list[s]=rmb_list[int(num_float_list[s])]+float_unit_list[s]#数字+单位
			if num_float_list[0]=='0' and num_float_list[1]!='0':#如果有零要去除单位
				rmb_num_float_list[0]=rmb_num_float_list[0][0]
			if num_float_list[0]=='0' and num_float_list[1]==' ':#如果角为0没有分去除小数
				rmb_num_float_list[0]=' '
			if num_float_list[1]=='0':#如果 分 单位为0 去除0
				rmb_num_float_list[1]=' '
	#结果
	for n in range(12):#循环12次,因为整数有12位
		if rmb_num_int_list[n]!=' ':#除了占位符外
			result_int+=rmb_num_int_list[n]#整数结果
		if '亿万'in result_int:#如果 亿 万 这两个单位在前面的程序中处理后连到了一块 将 亿万 替换成 亿
			result_int=result_int.replace('亿万','亿')
	for n in range(2):#循环2次,因为小数有2位
		if rmb_num_float_list[n]!=' ':#除了占位符外
			result_float+=rmb_num_float_list[n]#小数结果
	return result_int+zheng+result_float#数字转换成人民币大写的结果

#随机录用
def make_admission(word,interviewee_info,mailTmpl):#用word 制作 docx录用信 或 拒绝录用信 面试者信息 录用信模板
	date_obj,name,gender,qualification,mail,position,salary=interviewee_info

	TmplDir,TmplName=os.path.split(mailTmpl)#返回路径与文件名组成的元组 不含/
	AdmOrRej,suffix=os.path.splitext(TmplName)#返回文件前缀和后缀
	outDocxFolder=TmplDir+'\\mailsDocx'
	outPdfFolder=TmplDir+'\\mailsPdf'
	if not os.path.exists(outDocxFolder):
		os.mkdir(outDocxFolder)#如果输出文件夹不存在 创建 录用信Docx 文件夹
	if not os.path.exists(outPdfFolder):
		os.mkdir(outPdfFolder)#如果输出文件夹不存在 创建 录用信Pdf 文件夹
	outDocxFile=outDocxFolder+'\\{}_{}.docx'.format(AdmOrRej,name)
	outPdfFile=outPdfFolder+'\\{}_{}.pdf'.format(AdmOrRej,name)

	TmplDocx=word.Documents.Open(mailTmpl)#打开 模板docx
	#word.WebOptions.Encoding='utf-8'#设置docx编码格式 防止其他模块读取乱码 比如pyPDF3
	TmplDocx.SaveAs(outDocxFile)#复制保存模板
	TmplDocx.Close()#关闭 模板docx
	docx=word.Documents.Open(outDocxFile)#打开信件docx

	if AdmOrRej=='admission':#如果录用 生成录用信
		word.Selection.Find.Execute('Name', True, True, False, False, False, True, 1, False,name ,2)#用newstr替换oldstr
		if gender=='男':
			call='先生'
		else:
			call='女士'
		word.Selection.Find.Execute('Call', True, True, False, False, False, True, 1, False, call,2)
		word.Selection.Find.Execute('Department', True, True, False, False, False, True, 1, False, position[0],2)
		word.Selection.Find.Execute('Position', True, True, False, False, False, True, 1, False, position,2)
		word.Selection.Find.Execute('SalaryProbation', True, True, False, False, False, True, 1, False,str(salary-1000) ,2)
		word.Selection.Find.Execute('CapitalSalaryProbation', True, True, False, False, False, True, 1, False, numberToRmb(str(salary-1000)),2)
		word.Selection.Find.Execute('Salary', True, True, False, False, False, True, 1, False,str(salary) ,2)
		word.Selection.Find.Execute('CapitalSalary', True, True, False, False, False, True, 1, False,numberToRmb(str(salary)) ,2)
		RegisterDate=today_obj+datetime.timedelta(days=randint(1,2))
		word.Selection.Find.Execute('ProbationDateStart', True, True, False, False, False, True, 1, False, str(RegisterDate),2)
		word.Selection.Find.Execute('ProbationDateEnd', True, True, False, False, False, True, 1, False, str(RegisterDate+datetime.timedelta(days=30)),2)
		word.Selection.Find.Execute('RegisterDate', True, True, False, False, False, True, 1, False, str(RegisterDate),2)
		word.Selection.Find.Execute('AdmissionDate', True, True, False, False, False, True, 1, False, str(today_obj),2)
	else:#如果没被录用 生成拒绝录用信
		word.Selection.Find.Execute('Name', True, True, False, False, False, True, 1, False,name ,2)#用newstr替换oldstr
		if gender=='男':
			call='先生'
		else:
			call='女士'
		word.Selection.Find.Execute('Call', True, True, False, False, False, True, 1, False, call,2)
		word.Selection.Find.Execute('AdmissionDate', True, True, False, False, False, True, 1, False, str(today_obj),2)

	docx.Save()#保存
	docx.SaveAs(outPdfFile,FileFormat=17,Encoding=65001)#保存为pdf utf-8编码
	docx.Close()#关闭docx

	return outPdfFile#返回 信件pdf目录

def add_stamp(pdf_file, watermark_file):#给pdf每一页加上印章 印章pdf大小也为A4 分辨率842x595
	oriFile = open(pdf_file, 'rb')
	stampFile = open(watermark_file, 'rb')
	origin_reader = PyPDF3.PdfFileReader(oriFile)
	watermark_reader = PyPDF3.PdfFileReader(stampFile)
	writer = PyPDF3.PdfFileWriter()  # 打开两个pdf 新建一个pdf

	pdfDir,pdfName=os.path.split(pdf_file)#返回路径与文件名组成的元组 不含/
	prefix,suffix=os.path.splitext(pdfName)#返回文件前缀和后缀
	outPdfFolder=pdfDir+'\\mailsPdfStamped'
	if not os.path.exists(outPdfFolder):
		os.mkdir(outPdfFolder)#如果输出文件夹不存在 创建 已盖章的录用信pdf文件夹

	for n in range(origin_reader.numPages):  # 每页添加水印
		writer.addPage(origin_reader.getPage(n))
		writer.getPage(n).mergePage(watermark_reader.getPage(0))
	stampedFile = open(outPdfFolder + '\\'+prefix+'_stamped.pdf', 'wb')
	writer.write(stampedFile)  # 写入新建pdf

	oriFile.close()
	stampFile.close()
	stampedFile.close()  # 关闭三个pdf


intervieweeCount=10#面试者数量
xlsx_file='{}interviewees.xlsx'.format(intervieweeCount)#xlsx储存目录
Templates=['D:\\_CodePrograms_\\Python_Primary_Programs\\document_processing\\word_processing\\make_offers\\admission.docx','D:\\_CodePrograms_\\Python_Primary_Programs\\document_processing\\word_processing\\make_offers\\reject.docx']#录用信模板目录 拒绝录用信模板目录

build_xlsx(intervieweeCount,xlsx_file)#随机生成面试者信息 储存到xlsx_file

word=Dispatch('Word.Application')#打开word
for interviewee_info in get_info_list(xlsx_file):#为每个面试者生成信件
	add_stamp(make_admission(word,interviewee_info,choice(Templates)),r'stamp.pdf')
word.Quit()#退出word
